// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef CLOUDAPPSDK_CASDECODECONTROLLER_H
#define CLOUDAPPSDK_CASDECODECONTROLLER_H

#include <thread>
#include <atomic>
#include <unistd.h>
#include <android/native_window.h>
#include "CasLog.h"
#include "CasVideoEngineCommon.h"
#include "CasDecoder.h"

enum FramePatterns {
    FPSPATTERNS15 = 0,  // 0
    FPSPATTERNS24,      // 1
    FPSPATTERNS25,      // 2
    FPSPATTERNS30,      // 3
    FPSPATTERNS48,      // 4
    FPSPATTERNS60,      // 5
    FPSPATTERNS_MAX,
};

const char UPDATE_FPSPATTERNS[FPSPATTERNS_MAX][8] = {
        "4",        // 15 fps 丢3保1
        "32",       // 24 fps 丢1保1，丢2保1
        "32322",    // 25 fps 丢1保1，丢1保1，丢2保1，丢1保1，丢2保1
        "2",        // 30 fps 丢1保1
        "2111",     // 48 fps 保3，丢1保1
        "1",        // 60 fps 不丢
};

class CasDecodeController {
public:
    /*
     * @fn GetInstance
     * @brief to get CasDecodeController singleton
     */
    static CasDecodeController *GetInstance();

    /*
     * @fn DestroyInstance
     * @brief to release CasDecodeController singleton
     */
    static uint32_t DestroyInstance();

    /*
     * @fn Init
     * @brief to initialise CasDecodeController instance
     * @param[in] nativeWindow View to display of (type <tt>ANativeWindow *</tt>)
     * @param[in] type Method to decode, fixed to DECODER_TYPE_HW, of (type <tt>enum DecoderType</tt>)
     * @param[in] rotation degrees
     * @return errno: SUCCESS
     *                VIDEO_ENGINE_CLIENT_INIT_FAIL
     */
    uint32_t Init(ANativeWindow *nativeWindow, FrameType frameType, int rotationDegrees);

    /*
     * @fn Start
     * @brief to start CasDecodeController instance
     * @return errno: SUCCESS
     *                VIDEO_ENGINE_CLIENT_START_ERR
     */
    uint32_t Start();

    /*
     * @fn Decode
     * @brief CasDecodeController instance places H264 into CasDecoder
     * @param[in] buf Initial address of current frame of (type <tt>const int8_t *</tt>)
     * @param[in] length Current frame length of (type <tt>size_t</tt>)
     * @return errno: SUCCESS
     *                VIDEO_ENGINE_CLIENT_DECODE_ERR
     *                VIDEO_ENGINE_CLIENT_PARAM_INVALID
     */
    uint32_t Decode(const uint8_t *buf, size_t length);

    /*
     * @fn Stop
     * @brief to stop CasDecodeController instance
     * @return errno: SUCCESS
     *                VIDEO_ENGINE_CLIENT_STOP_ERR
     */
    uint32_t Stop();

    /*
     * @fn Destroy
     * @brief to destroy CasDecodeController instance
     */
    void Destroy() noexcept;

    /*
     * @fn SetSubThreadStatus
     * @brief to set CasDecodeController sub-thread work status
     * @param[in] status, true: sub-thread running, false: sub-thread stopped
     * @return void
     */
    void SetSubThreadStatus(bool status);

    /*
     * @fn IsSubThreadRunning
     * @brief to check if CasDecodeController sub-thread running
     * @return true: sub-thread running, false: sub-thread stopped
     */
    bool IsSubThreadRunning() const;

    /*
     * @fn GetStatus
     * @brief to get CasDecodeController instance status
     * @return EngineStat
     */
    EngineStat GetStatus() const;

    /*
     * @fn SetStatus
     * @brief to set CasDecodeController instance status
     * @param[in] stat CasDecodeController instance status, pass by value, of (type <tt>enum EngineStat</tt>)
     * @return void
     */
    void SetStatus(const EngineStat stat);

    /*
     * @fn IsStatus
     * @brief to judge CasDecodeController instance status
     * @param[in] stat CasDecodeController instance status, pass by value, of (type <tt>enum EngineStat</tt>)
     * @return bool
     */
    bool IsStatus(const EngineStat stat) const;

    /*
     * @fn GetStatics
     * @brief to get CasDecodeController instance statistics information
     * @return DecoderStatics
     */
    DecoderStatistics GetStatistics() const;

    /*
     * @fn OutputTaskEntry
     * @param[in] dCtr Instance of (type <tt>CasDecodeController *</tt>)
     * @brief act as a thread entry to output processed data and to display
     */
    friend void OutputTaskEntry(CasDecodeController *dCtr);

    /*
     *  first frameListener
     */
    void SetFirstFrameListener(CasFirstVideoFrameListener *listener);

    /*
     * decodeStat listener, get decoder time
     */
    void SetDecodeStatListener(CasVideoDecodeStatListener *listener);

    CasDecoder* GetCasDecoder();

    CasFirstVideoFrameListener* GetCasFirstVideoFrameListener();

    CasVideoDecodeStatListener* GetCasVideoDecodeStatListener();

    bool IsDiscardFrame(uint32_t updatePatternIdx);
    void ClearDiscardFrameCache();
    uint32_t ReduceFps(uint64_t frameBuffer);

    uint64_t m_InputFrameNum = 0;
    uint64_t m_DisplayFrameNum = 0;
    uint64_t m_DiscardFrameNum = 0;
    uint32_t m_UpdatePatternIdx = FPSPATTERNS60;
    uint32_t m_UpdatePatternOffset = 0;
    uint32_t m_HoldFrames = 0;

    bool isStartDiscard = false;        // 是否触发丢帧策略，进行日志打印

private:
    /*
     * @fn CasDecodeController
     * @brief constructor
     */
    CasDecodeController();

    /*
     * @fn CasDecodeController
     * @brief destructor
     */
    ~CasDecodeController();

    static CasDecodeController *g_instance;
    std::atomic<bool> m_subThreadRunning {false};
    std::atomic<EngineStat> m_engineStatus {EngineStat::ENGINE_INVALID};
    CasDecoder *m_decoder = nullptr;
    CasFirstVideoFrameListener *m_firstFrameListener = nullptr;
    CasVideoDecodeStatListener *m_decodeStatListener = nullptr;

};
#endif // CLOUDAPPSDK_CASDECODECONTROLLER_H